/**验证器，用于验证表单对象
 * @author yenbay
 * @description 
 * @version 2017.05.05 
 */
var validator = (function(){
    var _inner= {
    rule:{
        minLength: function(params){
                    return {
                        result: params[0].length >= params[1],
                        msg: "最小长度必须大于"+params[1]
                    }  
        },
        maxLength: function(params){
                    return {
                        result: params[0].length <= params[1],
                        msg: "最大长度不能超过"+params[1]
                    }
        },
        lengthBetween: function(params){
                    return {
                        result: (params[0].length >= params[1] && params[0].length <= params[2]),
                        msg: "长度必须在"+params[1]+"和"+params[2]+"之间"
                    }
        },
        required: function(params){
                    return {
                        result: (params[0] && params[0].length>0) || false,
                        msg: "此项为必须项"
                    }
        },
        number: function(params){
                    return {
                        result: /^\d*$/g.test(params[0]),
                        msg: "必须全部为数字"
                    }
        },
        custom: function(params){ 
            //console.info(new RegExp(params[1].replace(/[\.\*\+\{\}\|\$\^\\\/\?\[\]]/g,"\\$&"),"gm")); 
                    return {
                        result: new RegExp(params[1],"gm").test(params[0]),
                        msg: "不符合要求"
                    }
        },
        equalTo: function(params){
                    var another=params[1],suc=true;
                    if(another){
                        another=another.replace(/^\s*#|\s*$/g,"");
                        var dom=another && document.getElementById(another);
                        if(dom && dom.value){
                            suc = dom.value == params[0];
                        } 
                    } 
                    return {
                        result: suc,
                        msg: "两次输入不一致"
                    }
        }
    },
    tipPosition:"left right",
    changePosition: function(posStr){
        if(posStr) this.tipPosition=posStr;
    },
    optReg: /^\s*\{\s*([\w\d]+\s*\:[\d\w\-_#\\\/\$\(\)\+\*&\^\|\.@]+,{0,1}\s*|[\w\d]+\s*\:\s*\[(?:[\d\w\-_#\\\/\$\(\)\+\*&\^\|\.@]+,{0,1})+\],{0,1}\s*)+\s*\}\s*$/gmi,
    msgFilter: function(msg){
        var _msg = msg;
        if(msg && /[<]/.test(msg)){
           var exTag="p|div|body|table|html|script|link|style|table|td|tr|head|iframe|ul|li"; 
            _msg = _msg.replace(new RegExp("<("+exTag+")[^>]*\\/{0,1}>|</("+exTag+")>","gi"),"");  
        } 
        return _msg || "";
    }, 
	uuid: function() { 
	    var s = []; 
	    var hexDigits = "0123456789abcdef"; 
	    for (var i = 0; i < 36; i++) { 
	        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); 
	    } 
	    s[14] = "4";  // bits 12-15 of the time_hi_and_version field to 0010 
	    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);  // bits 6-7 of the clock_seq_hi_and_reserved to 01 
	    s[8] = s[13] = s[18] = s[23] = "-";  
	    var uuid = s.join(""); 
	    return uuid; 
	},
    pop:function(msg,element,timeout,relpos,offset){ 
        //清理旧的
        var me=this,pop_id="error_"+me.uuid();
        //删除旧的
        var oldId = element.getAttribute("error-id");
        var old = oldId && document.getElementById(oldId);
        if(old) {
        	me.remove(old);  
        }
        msg = me.msgFilter(msg);
         //元素上/下/左/右弹出
        var pos=me.getPosition(element); 
        if(offset){
            //偏移量
            pos.x=pos.x+offset.x;//245
            pos.y=pos.y+offset.y;//145
        }  
        var message=me.create(window,"div","validator-pop-default",pop_id); 
        element.setAttribute("error-id",pop_id); 
        document.body.appendChild(message);
       
        //消息内容
        var text=me.create(window,"p",null,null,null,msg);
        message.appendChild(text); 
        me.addEvent(text,"click",function(){
            me.remove(this.parentNode);
        }); 
        //定位 
        message.style.position="fixed";  
        message.style.left = (pos.x+pos.w)+"px";
        message.style.top = (pos.y)+"px"; 
        if(relpos) relpos.replace(/(\w+)[\W]+(\w+)/gi,function(s0,s1,s2){
            var h=s1.toLowerCase(),v=s2 && s2.toLowerCase();//h水平，v垂直  
            var _position = h+" "+v;  
            switch(_position){
                case "left top": 
                    message.style.left=pos.x+"px";
                    message.style.top=(pos.y-message.offsetHeight)+"px";
                    break;   
                case "left bottom":
                    message.style.left=pos.x+"px";
                    message.style.top=(pos.y+pos.h)+"px";
                    break;
                case "right top":
                    message.style.left=(pos.x+pos.w)+"px";
                    message.style.top=pos.y+"px";
                    break;
                case "right bottom":  
                    message.style.left=(pos.x+pos.w-message.offsetWidth)+"px";
                    message.style.top=(pos.y+pos.h)+"px";
                    break; 
                default:break;
            } 
        }); 
        //定时删除 
        if(timeout!=false) setTimeout(function(){
              me.remove(message);
        }, timeout && !isNaN(timeout+"") ? timeout : 1000); 
        return { 
        	element:message,
        	close:function(){
        		me.remove(this.element);
        	}
        };
    },  
    //判断是否是dom对象
    getObjectType:function(element){
        return Object.prototype.toString.call(element);
    },
    //添加元素
    create:function(_window,tagName,className,id,name,innerHtml){
        var ele=_window.document.createElement(tagName);
        if(id){ if(ele.id){ ele.id=id; }else ele.setAttribute("id",id); }
        if(name){ if(ele.name){ ele.name=name; }else ele.setAttribute("name",name); }
        if(id){ if(ele.id){ ele.id=id; }else ele.setAttribute("id",id); }
        if(className) ele.className=className;
        if(innerHtml) ele.innerHTML=innerHtml;
        return ele;
    },
    //删除元素
    remove: function(element){ 
        try{ 
            var _pNode=element && element.parentNode;
            if(_pNode) _pNode.removeChild(element); 
            return true;
        }catch(e){ 
            console.error(e);
        }
        return false; 
    },
    //获取窗口大小
    getWindowSize:function(_window){
        var winWidth = 0, winHeight = 0;
        //获取窗口宽度 
        if (_window.innerWidth) winWidth = _window.innerWidth; 
        else if ((_window.document.body) && (_window.document.body.clientWidth)) winWidth = _window.document.body.clientWidth; 
        // 获取窗口高度 
        if (_window.innerHeight) winHeight = _window.innerHeight; 
        else if ((_window.document.body) && (_window.document.body.clientHeight)) winHeight = _window.document.body.clientHeight; 
        return {"w":winWidth,"h":winHeight};
    },
    //获取元素位置左定点位置x,y，及元素盒子宽度，高度
    getPosition: function(element) { 
        var top = document.documentElement.clientTop,
        left = document.documentElement.clientLeft,
        rect = element.getBoundingClientRect();  
            return {
            y: rect.top - top, 
            x: rect.left - left,
            w: element.offsetWidth,
            h: element.offsetHeight
        };
    },
    //返回最大的z-index
    getMaxZIndex:function(_window){ 
        var zIndexMax=0; 
        var all = _window.document.all;
        for(var k=0;k<all.length;k++){ 
            var zIndex=all[k].style.zIndex; 
            zIndexMax=zIndex&&zIndex>zIndexMax?zIndex:zIndexMax;
        } 
        return zIndexMax;
    },
    isArray: function(obj){
        return /^\[object\s(array|htmlcollection)\]$/gi.test(Object.prototype.toString.call(obj));
    },
    //添加事件
    addEvent:function(elements,type,handler){ 
         var me=this,l=me.isArray(elements)? (elements.length || 0): -1;
            while(l>-2 && l--){
                var element= l<0 ? elements:elements[l];
                var types = (me.isArray(type) || typeof(type) != "string") ? type : type.trim().split(/\W+/),
                    k = types.length; 
                if (k) while (k--) {
                    var _type = types[k]; 
                    // 判断是否存在DOM2级方法
                    if(element.addEventListener) {
                        element.addEventListener(_type, handler, false);
                    } 
                    // 判断是否是IE版本
                    else if(element.attachEvent) {
                        element.attachEvent("on" + _type, handler);
                    } 
                    // 判断是否存在DOM0级方法
                    else {
                        element["on" + _type] = handler;
                    } 
                }
                element = null;
            } 
            elements=null;  
    },
    //移除事件
    removeEvent:function(elements,type,handler){ 
        var me=this,l=me.isArray(elements)? (elements.length || 0): -1;
            while(l>-2 && l--){
                var element= l<0 ? elements:elements[l];
                var types = (this.isArray(type) || typeof(type)!="string") ? type : type.trim().split(/\W+/),
                    k = types.length;
                if (k) while (k--) { 
                    var _type = types[k]; 
                    if(element.removeEventListener) {
                        element.removeEventListener(_type, handler, false);
                    } else if(element.detachEvent) {
                        element.detachEvent("on" + _type, handler);
                    } else {
                        element["on" + _type] = null;
                    }
                }
            }
    },  
    //绑定表单验证事件
    bind:function(element){ 
        var me=this;
        //先判断是否是可绑定元素input、select、textarea
        if(/\[\w+\shtml(input|select|textarea)element\]/gi.test(me.getObjectType(element))){
            //判断是否包含验证属性validate-option
            var opt=element.getAttribute("validate-option");
            if(element.type && /hidden|button|checkbox|radio/gi.test(element.type)) return;
            if(opt && new RegExp(me.optReg).test(opt)){
                //判断验证属性是否合法 
                //绑定,keyup
                me.addEvent(element,["keyup"],function(evt){ 
                    //触发验证事件 
                    me.validate(this);  
            		return false; 
            	}); 
            }
        }   
    },
    unbind:function(element){
        var me=this;
        //先判断是否是可绑定元素input、select、textarea
        if(/\[\w+\shtml(input|select|textarea)element\]/gi.test(me.getObjectType(element))){
            //判断是否包含验证属性validate-option
            var opt=element.getAttribute("validate-option");
            if(opt && new RegExp(me.optReg).test(opt)){
                //判断验证属性是否合法
                //解除绑定,keyup
                me.removeEvent(element,["keyup"],function(evt){ 
                    me.validate(this);  
            		return false; 
                });
            } 
        }   
    },
    bindAll:function(){
        try{
            var inputs=document.getElementsByTagName("input");
            var selects=document.getElementsByTagName("selects");
            var textareas=document.getElementsByTagName("textarea");
            for(var k=0;k<inputs.length;k++) { this.bind(inputs[k]);}
            for(var j=0;j<selects.length;j++) { this.bind(selects[j]);}
            for(var i=0;i<textareas.length;i++) { this.bind(textareas[i]);} 
        }catch(e){
            console.warn(e);
        }
    }, 
    findChildInput:function(node){
        var childs=new Array(),me=this,_childs=node.childNodes;
        for(var i=0;i<_childs.length;i++){
            if(/\[object\shtml(input|textarea|select)element\]/gi.test(me.getObjectType(_childs[i]))){
                childs.push(_childs[i]);
            }else{ 
                 childs.concat(me.findChildInput(_childs[i]));
            }
        }
        return childs;
    }, 
    validateForm:function(form){
        var childs= this.findChildInput(form),success=true;  
        for(var k=0;k< childs.length;k++) {
            try{ 
                if(/\[object\shtml(input|textarea|select)element\]/gi.test(this.getObjectType(childs[k]))){
                    var _success=this.validate(childs[k]);
                    if(_success!=true) success = _success;
                }
            }catch(e){
                 console.error(e);
            }
        }  
        return success;
    },
    validate_check:function(element){ 
        var alls = document.getElementsByName(element.name),me=this;
        var success=true,check_val = [],params=new Array(),value="";
        if(alls) for(var k in alls){
            if(alls[k].checked) check_val.push(alls[k].value);
        }
        if(check_val && check_val.length>0) value="_required_true";//判定必选一项
        params.push(value); 
        var errorMsg="";
        element.className = element.className.replace(" validator-pop-error","");
        var _old=element.getAttribute("error-id"),old=_old && document.getElementById(_old);
        if(old) me.remove(old);
        if(/required\s*\:\s*\S*/gi.test(element.getAttribute("validate-option"))){
            try{
                 var res=me.rule.required(params);
                 if(!res.result){
                    success=res.result;
                    errorMsg=res.msg;
                 }
            }catch(e){
                console.error(e);
            }
        }
        //解决结果
        if(!success){
                _inner.pop(errorMsg || "输入值不合法！",element,false,me.tipPosition);   
                element.className += " validator-pop-error";
                element.focus();
        }
        return success; 
    },
    validate:function(element){ 
        var success=true,me=this,options=element && element.getAttribute("validate-option"),tagName=element.tagName.toLowerCase(),type=element.type || "",value=element.value||"";
        //匹配规则validate-option="{minLength:[30],maxLength:60}"
        type = type.trim().toLowerCase();
        //过滤button
        if(type =="button") return success;
        if(type =="hidden") return success;  
        if(!new RegExp(me.optReg).test(options)){
             console.error(element); 
             throw new Error("validate 验证表达式不合法！"); 
             return success;
        }    
         //先清理掉错误标记样式
        element.className = element.className.replace(" validator-pop-error","");
        var _old=element.getAttribute("error-id"),old=_old && document.getElementById(_old);
        if(old) me.remove(old);
        var errorMsg=""; 
        //判定checkbox
        if(tagName =="input" && /checkbox|radio/gi.test(type)){
                //input例外   
                return me.validate_check(element);  
        } 
        options.replace(/([\w\d]+)\s*\:\s*([\w\d\-_\\\/\$\@\&\%\*\(\)\+\^\.\?\|']+|\[([^\]]+)\])/gi,function(s0,s1,s2,s3){
            var  params=new Array(),  
                 fun=me.rule[s1],//s1为方法名
                 pStr=s3 || s2;//方法参数
            var _pArray=  pStr && pStr.split(",");  
            //加载值
            params.push(value);   
            //加载参数
            params = params.concat(_pArray);  
            //执行判定 
            try{
                if(fun){ 
                    var _result=fun(params);
                    if(!_result.result){
                        success=false; 
                        errorMsg=_result.msg;
                    } 
                }
            }catch(e){
                console.error(e);
            } 
        }); 
        //判定  
        if(!success){ 
                var msg=_inner.pop(errorMsg || "输入值不合法！",element,false,me.tipPosition);  
                element.className += " validator-pop-error";
                element.focus();
        } 
        return success;
    }
};  
//加载页面验证绑定
_inner.bindAll();
return { 
    //验证表单或者单个表单对象
    validate: function(element){  
        if(/\[object\shtmlformelement\]/gi.test(_inner.getObjectType(element))){
            //form对象，遍历验证所有input/select/textarea
            return _inner.validateForm(element);
        }else if(/\[object\shtml(input|textarea|select)element\]/gi.test(_inner.getObjectType(element))){
            //判断是否包含验证属性validate-option
            var opt=element.getAttribute("validate-option");
            if(opt && /[\s\S]+/gi.test(opt)){
                 return _inner.validate(element);
            }else{
                 //没有验证属性，则默认通过，不验证 
                 console.warn("validate方法调用错误，对象缺少属性！",element);  
            } 
        }else throw new Error("validate方法调用失败，不支持的对象！"+JSON.stringify(form));
        return true;
    },
    bind:function(element){
        _inner.bind(element);
    },
    unbind:function(element){
        _inner.unbind(element);
    } 
};
})();
 
